package com.myssm.myspringmvc;

import com.myssm.util.StringUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Jack Tang
 * @description
 * @create 2023-05-07 19:22
 */
@WebServlet("*.do") // 拦截所有以 .do 结尾的请求
public class DispatcherServlet extends HttpServlet {

    private Map<String,Object> beanMap = new HashMap<>();
    public DispatcherServlet(){


    }

    public void init(){
        try {
            InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
            // 1.创建 DocumentBuilderFactory 对象
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            // 2.创建 DocumentBuilder 对象
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            // 3.创建 Document 对象
            Document document = documentBuilder.parse(inputStream);

            // 4.获取所有的 bean 节点
            NodeList beaNodeList = document.getElementsByTagName("bean");
            for (int i = 0; i < beaNodeList.getLength(); i++) {
                Node beaNode = beaNodeList.item(i);
                if (beaNode.getNodeType() == Node.ELEMENT_NODE) {
                    Element beaElement = (Element) beaNode;
                    // id属性通常用于标识这个元素
                    String beanId = beaElement.getAttribute("id");
                    // class属性指定了这个元素对应的Java类的名字
                    String className = beaElement.getAttribute("class");
                    // 动态加载指定名字的Java类
                    Class controllerBeanClass = Class.forName(className);
                    // 使用controllerBeanClass.newInstance()来创建这个类的一个新的实例
                    Object beanObj = controllerBeanClass.newInstance();
                    Method setServletContextMethod = controllerBeanClass.getDeclaredMethod("setServletContext", ServletContext.class);

                    setServletContextMethod.invoke(beanObj,this.getServletContext());
                    //将新创建的Java对象加入到beanMap中，以bean元素的id作为键
                    beanMap.put(beanId, beanObj);
                }
            }

        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (SAXException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置编码
        req.setCharacterEncoding("utf-8");

        // 假设url是： http://localhost:8080/day8/hello.do
        //那么 servletPath 是: /hello.do
        // 具体思路是：
        // 第一步： 通过 getServletPath() 这个函数获取到了 /hello.do
        // 第二步： 通过字符串截取得到 hello
        // 第三步： 让 hello 和 HelloController 能对应上
        String servletPath = req.getServletPath();  //通过这个函数获取到了 /hello.do
        //通过字符串截取得到 hello
        int lastDotIndex = servletPath.lastIndexOf(".do");
        servletPath = servletPath.substring(1, lastDotIndex);

        Object controllerBeanObj = beanMap.get(servletPath);


        String operate = req.getParameter("operate");
        if (StringUtil.isEmpty(operate)){
            operate = "index";
        }

        try {
            // 获取名为 "operate" 的方法
            Method method = controllerBeanObj.getClass().getDeclaredMethod(operate,HttpServletRequest.class,HttpServletResponse.class);
            if (method != null) {
                //将 Method 对象的 accessible 标志设置为 true，从而可以访问该方法，即使该方法是私有的或受保护的。
                method.setAccessible(true);
                // m.invoke() 方法会将 this 对象作为方法的调用者，也就是说，该方法会在当前对象上执行。
                method.invoke(controllerBeanObj, req, resp);
            }else {
                throw new RuntimeException("operate的值非法！");
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}